/******************************************************************************
 * Copyright (C) 2013 ShenZhen ComTop Information Technology Co.,Ltd
 * All Rights Reserved.
 * 本软件为深圳康拓普开发研制。未经本公司正式书面同意，其他任何个人、团体不得使用、
 * 复制、修改或发布本软件.
 *****************************************************************************/

package com.code.common.util;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;

import org.apache.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * RSA加密工具类
 * <pre>
 * 提供RSA加密解密的工具方法
 * </pre>
 * @author 陈星凌
 * @since TOP4.0
 * @version 2013-1-30 陈星凌 由原TOP系统迁移,由RSATools改名为RSAUtil
 */
public final class RSAUtil {

	/**
	 * 构造函数
	 */
	private RSAUtil() {
	}

	/** logger对象 */
	private static final Logger LOGGER = Logger.getLogger(RSAUtil.class);

	/** 加密计数 */
	private static int encryptCount = 0;

	/** 加密密码 */
	private static Cipher encryptCipher = null;

	/** 解密计数 */
	private static int decryptCount = 0;

	/** 解密密码 */
	private static Cipher decryptCipher = null;

	/**
	 * 生成密钥对
	 * 
	 * @return KeyPair
	 */
	public static KeyPair generateKeyPair() {
		try {
			KeyPairGenerator objKeyPairGen = KeyPairGenerator.getInstance(
					"RSA", new BouncyCastleProvider());
			final int iKeySize = 128;
			objKeyPairGen.initialize(iKeySize, new SecureRandom());
			return objKeyPairGen.genKeyPair();
		} catch (Exception e) {
			LOGGER.error("生成密钥对出错：", e);
			return null;
		}
	}

	/**
	 * 生成公钥
	 * 
	 * @param modulus
	 *            byte[]
	 * @param publicExponent
	 *            byte[]
	 * @return RSAPublicKey
	 */
	public static RSAPublicKey generateRSAPublicKey(byte[] modulus,
			byte[] publicExponent) {
		KeyFactory objKeyFac = null;
		try {
			objKeyFac = KeyFactory.getInstance("RSA",
					new BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			LOGGER.error("无此产生公钥的算法：", ex);
			return null;
		}

		RSAPublicKeySpec objPubKeySpec = new RSAPublicKeySpec(new BigInteger(
				modulus), new BigInteger(publicExponent));
		try {
			return (RSAPublicKey) objKeyFac.generatePublic(objPubKeySpec);
		} catch (InvalidKeySpecException ex) {
			LOGGER.error("生成公钥的参数不合法：", ex);
			return null;
		}
	}

	/**
	 * 生成私钥
	 * 
	 * @param modulus
	 *            byte[]
	 * @param privateExponent
	 *            byte[]
	 * @return RSAPrivateKey
	 */
	public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
			byte[] privateExponent) {
		KeyFactory objKeyFac = null;
		try {
			objKeyFac = KeyFactory.getInstance("RSA",
					new BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			LOGGER.error("无此产生私钥的算法：", ex);
			return null;
		}

		RSAPrivateKeySpec objPriKeySpec = new RSAPrivateKeySpec(new BigInteger(
				modulus), new BigInteger(privateExponent));
		try {
			return (RSAPrivateKey) objKeyFac.generatePrivate(objPriKeySpec);
		} catch (InvalidKeySpecException ex) {
			LOGGER.error("生成私钥的参数不合法：", ex);
			return null;
		}
	}

	/**
	 * 序列化加密数据
	 * 
	 * @param key
	 *            Key 加密的密钥
	 * @param data
	 *            byte[] 待加密的明文数据
	 * @return byte[] 加密后的数据
	 */
	synchronized public static byte[] encrypt(Key key, byte[] data) {
		Key objTempKey = key;
		try {
			AbstractCreateRSAKey objCereateKey = getCreateRSAKeyInstance();
			if (objCereateKey != null) {
				objTempKey = objCereateKey.createRSAPrivateKey();
			}
		} catch (Exception e) {
			LOGGER.error("自定义创建密钥时出错！", e);
		}
		try {
			if (encryptCount == 0) {
				encryptCipher = Cipher.getInstance("RSA",
						new BouncyCastleProvider());
				encryptCipher.init(Cipher.ENCRYPT_MODE, objTempKey);
				encryptCount++;
			}
			int iBlockSize = encryptCipher.getBlockSize();// 获得加密块大小
			int iOutputSize = encryptCipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int iLeavedSize = data.length % iBlockSize;
			int iBlocksSize = iLeavedSize != 0 ? data.length / iBlockSize + 1
					: data.length / iBlockSize;
			byte[] byteRaw = new byte[iOutputSize * iBlocksSize];
			int i = 0;
			while (data.length - i * iBlockSize > 0) {
				if (data.length - i * iBlockSize > iBlockSize) {
					encryptCipher.doFinal(data, i * iBlockSize, iBlockSize,
							byteRaw, i * iOutputSize);
				} else {
					encryptCipher.doFinal(data, i * iBlockSize, data.length - i
							* iBlockSize, byteRaw, i * iOutputSize);
				}
				// 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中，
				// 而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
				i++;
			}
			return byteRaw;
		} catch (Exception e) {
			LOGGER.error("加密出错：", e);
			return null;
		}
	}

	/**
	 * 数据加密
	 * 
	 * @param key
	 *            加密的密钥
	 * @param data
	 *            待加密的明文数据
	 * @return 加密后的数据
	 */
	public static String encrypt(Key key, String data) {

		String strDestination = "";
		try {
			if (StringUtil.isNotBlank(data)) {
				strDestination = bytesToHexString(RSAUtil.encrypt(key,
						data.getBytes("UTF-8")));
			}
		} catch (UnsupportedEncodingException e) {
			LOGGER.error("编码错误：", e);
			return null;
		}
		return strDestination;
	}

	/**
	 * 序列化解密后的明文
	 * 
	 * @param key
	 *            解密的密钥
	 * @param raw
	 *            已经加密的数据
	 * @return 解密后的明文
	 */
	synchronized public static byte[] decrypt(Key key, byte[] raw) {
		Key objTempKey = key;
		try {
			AbstractCreateRSAKey objCereateKey = getCreateRSAKeyInstance();
			if (objCereateKey != null) {
				objTempKey = objCereateKey.createRSAPublicKey();
			}
		} catch (Exception e) {
			LOGGER.error("自定义创建密钥时出错！", e);
		}
		try {
			if (decryptCount == 0) {
				decryptCipher = Cipher.getInstance("RSA",
						new BouncyCastleProvider());
				decryptCipher.init(Cipher.DECRYPT_MODE, objTempKey);
				decryptCount++;
			}
			int iBlockSize = decryptCipher.getBlockSize();
			ByteArrayOutputStream objBout = new ByteArrayOutputStream(64);
			int j = 0;

			while (raw.length - j * iBlockSize > 0) {
				objBout.write(decryptCipher.doFinal(raw, j * iBlockSize,
						iBlockSize));
				j++;
			}
			return objBout.toByteArray();
		} catch (Exception e) {
			LOGGER.error("解密出错：", e);
			return null;
		}
	}

	/**
	 * 密码解密
	 * 
	 * @param key
	 *            解密的密钥
	 * @param raw
	 *            已经加密的数据
	 * @return 解密后的明文
	 */
	public static String decrypt(Key key, String raw) {
		String strDestination = "";
		try {
			if (StringUtil.isNotBlank(raw)) {
				byte[] bKey = RSAUtil.decrypt(key, hexStringToByte(raw));
				if (bKey != null && bKey.length > 0) {
					strDestination = new String(bKey, "UTF-8");
				} else {
					LOGGER.error("已经加密的数据raw：[" + raw + "]解密出错");
				}
			}
		} catch (UnsupportedEncodingException e) {
			LOGGER.error("字符串编码错误：", e);
			return null;
		}
		return strDestination;
	}

	/**
	 * 把字节数组转换成16进制字符串
	 * 
	 * @param bArray
	 *            字符数组
	 * @return 16进制字符串
	 */
	public static final String bytesToHexString(byte[] bArray) {
		StringBuffer sbHex = new StringBuffer(bArray.length);
		String sTemp;
		for (int i = 0; i < bArray.length; i++) {
			sTemp = Integer.toHexString(0xFF & bArray[i]);
			if (sTemp.length() < 2) {
				sbHex.append(0);
			}
			sbHex.append(sTemp.toUpperCase());
		}
		return sbHex.toString();
	}

	/**
	 * 把16进制字符串转换成字节数组
	 * 
	 * @param hex
	 *            16进制字符串
	 * @return 字符数组
	 */
	public static byte[] hexStringToByte(String hex) {
		int iLen = hex.length() / 2;
		byte[] byteResult = new byte[iLen];
		char[] chAchar = hex.toCharArray();
		for (int i = 0; i < iLen; i++) {
			int iPos = i * 2;
			byteResult[i] = (byte) (toByte(chAchar[iPos]) << 4 | toByte(chAchar[iPos + 1]));
		}
		return byteResult;
	}

	/**
	 * 把16字符转换成字节
	 * 
	 * @param c
	 *            16字符
	 * @return 字节
	 */
	private static byte toByte(char c) {
		byte byteTo = (byte) "0123456789ABCDEF".indexOf(c);
		return byteTo;
	}

	/**
	 * 读取配置项配置的创建密钥的类名，并通过反射得到实例。
	 * 
	 * @return 创建密钥的类实例
	 */
	private static AbstractCreateRSAKey getCreateRSAKeyInstance() {
		AbstractCreateRSAKey objCreateRSAKey = null;
		try {
			// 后续需要进行修改
			String strClassName = "N";

			// 配置项配置的值为N时，表示不需要自定义密钥。
			if ("N".equalsIgnoreCase(strClassName)
					|| "".equalsIgnoreCase(strClassName)) {
				return null;
			}

			// 通过反射，得到创建密钥的类实例。
			Class<?> objC = Class.forName(strClassName);
			objCreateRSAKey = (AbstractCreateRSAKey) objC.newInstance();

		} catch (Exception e) {
			LOGGER.error("生成创建密钥的类实例时出错！请检查ct-config.xml里的createRSAKey配置是否正确，",
					e);
		}
		return objCreateRSAKey;
	}
}